package com.zx.core.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * SQL中IN条件批处理工具类
 * <p>
 * 解决数据库SQL查询中IN参数超过限制（如Oracle中超过1000个）时报错的问题
 * 适用于SELECT、UPDATE、DELETE等各种SQL操作中的IN条件批量处理
 *
 * <p>
 * 使用示例:
 * <pre>
 * // 示例1: 批量查询用户信息
 * List<String> userIds = getUserIds(); // 假设有1500个用户ID
 * SqlInBatchUtils.batchProcess(userIds, SqlInBatchUtils.MAX_IN_SIZE, batch -> {
 *     List<User> users = userMapper.selectBatchByIds(batch);
 *     // 处理查询结果
 *     processUsers(users);
 * });
 *
 * // 示例2: 批量更新用户状态
 * List<String> userIds = getUserIds(); // 假设有2000个用户ID
 * SqlInBatchUtils.batchProcess(userIds, SqlInBatchUtils.MAX_IN_SIZE, batch -> {
 *     userMapper.updateStatusBatch(batch, UserStatus.ACTIVE);
 * });
 *
 * // 示例3: 批量删除用户并收集结果
 * List<String> userIds = getUserIds(); // 假设有1200个用户ID
 * List<Integer> deleteCounts = SqlInBatchUtils.batchProcessWithResults(
 *     userIds,
 *     SqlInBatchUtils.MAX_IN_SIZE,
 *     batch -> userMapper.deleteBatch(batch)
 * );
 * int totalDeleted = deleteCounts.stream().mapToInt(Integer::intValue).sum();
 *
 * // 示例4: 并行处理大数据集
 * List<String> userIds = getUserIds(); // 假设有10000个用户ID
 * SqlInBatchUtils.batchProcessParallel(userIds, SqlInBatchUtils.MAX_IN_SIZE, batch -> {
 *     userMapper.processLargeBatch(batch);
 * });
 * </pre>
 * </p>
 *
 * @author zhou xun
 * @since 2025-11-15
 */
public class SqlInBatchUtils {

    /**
     * 数据库IN查询参数最大限制数
     */
    public static final int MAX_IN_SIZE = 1000;

    /**
     * 分批处理大量数据，解决SQL查询IN参数超过数据库限制问题
     *
     * @param data      需要分批处理的数据
     * @param batchSize 批处理大小，建议不超过{@link #MAX_IN_SIZE}
     * @param consumer  处理每批数据的消费者函数
     * @param <T>       数据类型
     */
    public static <T> void batchProcess(List<T> data, int batchSize, Consumer<List<T>> consumer) {
        // 检查参数是否合法
        if (null == data || data.isEmpty() || batchSize <= 0 || null == consumer) {
            return;
        }

        // 限制批处理大小不超过最大限制
        final int actualBatchSize = Math.min(batchSize, MAX_IN_SIZE);

        // 分批处理数据
        partition(data, actualBatchSize).forEach(consumer);
    }

    /**
     * 分批处理大量数据并收集结果
     *
     * @param data      需要分批处理的数据
     * @param batchSize 批处理大小，建议不超过{@link #MAX_IN_SIZE}
     * @param function  处理每批数据的函数
     * @param <T>       输入数据类型
     * @param <R>       返回数据类型
     * @return 所有批处理结果的列表
     */
    public static <T, R> List<R> batchProcessWithResults(List<T> data, int batchSize, Function<List<T>, R> function) {
        // 检查参数是否合法
        if (null == data || data.isEmpty() || batchSize <= 0 || null == function) {
            return new ArrayList<>();
        }

        List<R> results = new ArrayList<>();
        final int actualBatchSize = Math.min(batchSize, MAX_IN_SIZE);

        partition(data, actualBatchSize).forEach(batch -> {
            R result = function.apply(batch);
            results.add(result);
        });

        return results;
    }

    /**
     * 并行分批处理大量数据
     *
     * @param data      需要分批处理的数据
     * @param batchSize 批处理大小，建议不超过{@link #MAX_IN_SIZE}
     * @param consumer  处理每批数据的消费者函数
     * @param <T>       数据类型
     */
    public static <T> void batchProcessParallel(List<T> data, int batchSize, Consumer<List<T>> consumer) {
        // 检查参数是否合法
        if (null == data || data.isEmpty() || batchSize <= 0 || null == consumer) {
            return;
        }

        // 限制批处理大小不超过最大限制
        final int actualBatchSize = Math.min(batchSize, MAX_IN_SIZE);

        // 创建并行流进行处理
        partition(data, actualBatchSize)
                .parallel()
                .forEach(consumer);
    }

    /**
     * 将列表分批转换为Stream
     *
     * @param data      需要分批处理的数据
     * @param batchSize 批处理大小
     * @param <T>       数据类型
     * @return 分批后的Stream
     */
    public static <T> Stream<List<T>> partition(List<T> data, int batchSize) {
        if (null == data || data.isEmpty() || batchSize <= 0) {
            return Stream.empty();
        }

        final int actualBatchSize = Math.min(batchSize, MAX_IN_SIZE);

        return Stream.iterate(0, i -> i < data.size(), i -> i + actualBatchSize)
                .map(i -> {
                    int endIndex = Math.min(i + actualBatchSize, data.size());
                    return data.subList(i, endIndex);
                });
    }
}